package ui;

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;

import javax.swing.BoxLayout;
import javax.swing.InputVerifier;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

/**
 * A Panel that contains verifies the start and end date
 * as well as start and end time.
 * 
 * @author Sean Young
 * @version 1.0
 *
 */
public class DateStartEndPanel extends JPanel{
	private JPanel inputPanel, validationPanel;
	private Calendar currentDate;
	public JTextField dateStart, dateEnd, timeStart, timeEnd;
	private boolean validDateStart = true, validDateEnd = true, validTimeStart = true, validTimeEnd = true;
	private JLabel validationLabel;
	private DateVerifier dateVerifier = new DateVerifier();
	

	public DateStartEndPanel() {
		super();
		setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
		inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		currentDate = Calendar.getInstance();
		DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
		DateFormat timeFormat = new SimpleDateFormat("hh:mma");
		
		dateStart = new JTextField(dateFormat.format(currentDate.getTime()), 10);
		dateStart.setInputVerifier(dateVerifier);
		dateStart.addActionListener(dateVerifier);
		
		timeStart = new JTextField(timeFormat.format(currentDate.getTime()), 7);
		timeStart.setInputVerifier(dateVerifier);
		timeStart.addActionListener(dateVerifier);
		
		currentDate.add(Calendar.HOUR_OF_DAY, 1);
		dateEnd = new JTextField(dateFormat.format(currentDate.getTime()), 10);
		dateEnd.setInputVerifier(dateVerifier);
		dateEnd.addActionListener(dateVerifier);
		
		timeEnd = new JTextField(timeFormat.format(currentDate.getTime()), 7);
		timeEnd.setInputVerifier(dateVerifier);
		timeEnd.addActionListener(dateVerifier);
		
		validationPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		validationLabel = new JLabel("   ");
		validationLabel.setForeground(Color.RED);
		validationPanel.add(validationLabel);
		
		inputPanel.add(dateStart);
		inputPanel.add(timeStart);
		inputPanel.add(new JLabel("to"));
		inputPanel.add(timeEnd);
		inputPanel.add(dateEnd);
		
		add(inputPanel);
		add(validationPanel);
	}
	/**
	 * Returns whether the panel has a valid start date
	 * @return whether or not the start date is valid.
	 */
	public boolean hasValidStartDate() {
		return validDateStart;
	}
	
	/**
	 * Returns whether all fields contain valid values.
	 * @return whether all fields contain valid values.
	 */
	public boolean hasValidDates() {
		return (validDateStart && validDateEnd && validTimeStart && validTimeEnd);
	}
	
	/**
	 * Returns whether the given string is a properly formatted date.
	 * A valid date format is "MM/dd/yyyy."
	 * @param date The string to be checked
	 * @return The validity of the string.
	 */
	public boolean isValidDateFormat(String date) {
		if(date == null) return false;
		SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
		sdf.setLenient(false);
		
		try {
			Date validDate = sdf.parse(date);
			return true;
		} catch (ParseException e) {
			return false;
		}
	}
	
	/**
	 * Returns whether the given string is a properly formatted time.
	 * A valid time format is "hh:mm (AM/PM)."
	 * @param time The string to be checked
	 * @return The validity of the string
	 */
	public boolean isValidTimeFormat(String time) {
		if(time == null) return false;
		SimpleDateFormat sdf = new SimpleDateFormat("hh:mma");
		sdf.setLenient(false);
		
		try {
			Date validDate = sdf.parse(time);
			return true;
		} catch (ParseException e) {
			return false;
		}
	}
	

	/**
	 * Parses the given fields and attempts to return 
	 * a Start Date with the data specified in the fields.
	 * @return The start date
	 */
	public Date returnStartDate() {
		int month, day, year, hour, minute;
		String startDateStr = dateStart.getText();
		String[] startMDY = startDateStr.split("/");
		String timeStr = timeStart.getText();
		String suffix = timeStr.substring(timeStr.length() - 2);
		timeStr = timeStr.substring(0, timeStr.length() - 2);
		String[] startHS = timeStr.split(":");
		
		month = Integer.parseInt(startMDY[0]);
		day = Integer.parseInt(startMDY[1]);
		year = Integer.parseInt(startMDY[2]);
		hour = Integer.parseInt(startHS[0]);
		minute = Integer.parseInt(startHS[1]);
		if(suffix.equalsIgnoreCase("pm")) hour += 12;
		if(suffix.equalsIgnoreCase("am") && hour == 12) hour = 0;
		
		Calendar c = new GregorianCalendar(year, month - 1, day, hour, minute, 0);
		return c.getTime();
		
	}
	
	/**
	 * Parses the given fields and attempts to return 
	 * an End Date with the data specified in the fields.
	 * @return The end date
	 */
	public Date returnEndDate() {
		int month, day, year, hour, minute;
		String endDateStr = dateEnd.getText();
		String[] endMDY = endDateStr.split("/");
		String timeStr = timeEnd.getText();
		String suffix = timeStr.substring(timeStr.length() - 2);
		timeStr = timeStr.substring(0, timeStr.length() - 2);
		String[] endHS = timeStr.split(":");
		
		month = Integer.parseInt(endMDY[0]);
		day = Integer.parseInt(endMDY[1]);
		year = Integer.parseInt(endMDY[2]);
		hour = Integer.parseInt(endHS[0]);
		minute = Integer.parseInt(endHS[1]);
		if(suffix.equalsIgnoreCase("pm")) hour += 12;
		if(suffix.equalsIgnoreCase("am") && hour == 12) hour = 0;
		
		Calendar c = new GregorianCalendar(year, month - 1, day, hour, minute, 0);
		return c.getTime();
		
	}
	/**
	 * Resets the panel.
	 */
	public void reset() {
		currentDate = Calendar.getInstance();
		DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
		DateFormat timeFormat = new SimpleDateFormat("hh:mma");
	
		dateStart.setText(dateFormat.format(currentDate.getTime()));
		dateStart.setBackground(Color.WHITE);
		validDateStart = true;
		
		timeStart.setText(timeFormat.format(currentDate.getTime()));
		timeStart.setBackground(Color.WHITE);
		validTimeStart = true;
		
		currentDate.add(Calendar.HOUR_OF_DAY, 1);
		
		dateEnd.setText(dateFormat.format(currentDate.getTime()));
		dateEnd.setBackground(Color.WHITE);
		validDateEnd = true;
		
		timeEnd.setText(timeFormat.format(currentDate.getTime()));
		timeEnd.setBackground(Color.WHITE);
		validTimeEnd = true;
		
		validationLabel.setText("   ");
	}
	
	/**
	 * Verifies all fields to ensure that fields are valid and do not
	 * conflict with one another.
	 */
	public class DateVerifier extends InputVerifier implements ActionListener {
		
		/**
		 * Verifies the validity of the field by checking
		 * a number of conditions based upon the component.
		 * @param comp The component to check
		 * @return The validity of the input
		 */
		@Override
		public boolean verify(JComponent comp) {
			return checkComponent(comp);
		}
		
		/**
		 * Returns the validity of the field based upon the
		 * component. The checks are unique to the component
		 * @param comp The component to check
		 * @return The validity of the input
		 */
		public boolean checkComponent(JComponent comp) {
			if(comp == dateStart) {
				validDateStart = isValidStartDate();
				return validDateStart;
			} else if (comp == timeStart) {
				validTimeStart = isValidStartTime();
				return validTimeStart;
			} else if (comp == dateEnd) {
				validDateEnd = isValidEndDate();
				return validDateEnd;
			} else if (comp == timeEnd) {
				validTimeEnd = isValidEndTime();
				return validTimeEnd;
			} else {
				return false;
			}
		}
		
		/**
		 * Determines whether the given date is valid. The method
		 * checks the format of the date as well as the validity 
		 * of the time and whether the date comes before the 
		 * end date and time if the current end date and time is 
		 * valid.
		 * @return The validity of the date
		 */
		public boolean isValidStartDate() {
			try {
				if(!(isValidDateFormat(dateStart.getText()))) {
					validationLabel.setText("Incorrect Start Date Format. Required Format(MM/DD/YYYY)");
					return false;
				}
				
				if(isValidTimeFormat(timeStart.getText()) && validDateEnd && validTimeEnd) {
					if(returnStartDate().after(returnEndDate())) {
						validationLabel.setText("The Start Date must be before the End Date.");
						timeStart.setBackground(Color.PINK);
						validTimeStart = false;
						return false;
					}
					if(returnStartDate().equals(returnEndDate())) {
						validationLabel.setText("The Start Date cannot be equal to the End Date.");
						timeStart.setBackground(Color.PINK);
						validTimeStart = false;
						return false;
					}
				}
				if(isValidTimeFormat(timeStart.getText())) {
					validTimeStart = true;
					timeStart.setBackground(Color.WHITE);
				}
				return true;
			} catch(NumberFormatException e) {
				validationLabel.setText("Incorrect Start Date Format. Required Format(MM/DD/YYYY)");
				return false;
			}
		}
		
		/**
		 * Determines whether the given time is valid. The method
		 * checks the format of the time as well as the validity 
		 * of the date and whether the date comes before the 
		 * end date and time if the current end date and time is 
		 * valid.
		 * @return The validity of the time
		 */
		public boolean isValidStartTime() {
			try {
				if(!isValidTimeFormat(timeStart.getText())) {
					validationLabel.setText("Incorrect Start Time Format. Required Format(HH:MM(AM/PM))");
					return false;
				}
				if(isValidDateFormat(dateStart.getText()) && validDateEnd && validTimeEnd) {
					if(returnStartDate().after(returnEndDate())) {
						validationLabel.setText("The Start Date must be before the End Date.");
						validDateStart = false;
						dateStart.setBackground(Color.PINK);
						return false;
					}
					if(returnStartDate().equals(returnEndDate())) {
						validationLabel.setText("The Start Date cannot be equal to the End Date.");
						validDateStart = false;
						dateStart.setBackground(Color.PINK);
						return false;
					}
				}
			if(isValidDateFormat(dateStart.getText())) {
				validDateStart = true;
				dateStart.setBackground(Color.WHITE);
			}
			return true;
			} catch(NumberFormatException e) {
				validationLabel.setText("Incorrect Start Time Format. Required Format(HH:MM(AM/PM))");
				return false;
			}
		}
		
		/**
		 * Determines whether the given date is valid. The method
		 * checks the format of the date as well as the validity 
		 * of the time and whether the date comes after the 
		 * start date and time if the current start date and time is 
		 * valid.
		 * @return The validity of the date
		 */
		public boolean isValidEndDate() {
			try {
				if(!isValidDateFormat(dateEnd.getText())) {
					validationLabel.setText("Incorrect End Date Format. Required Format(MM/DD/YYYY)");
					return false;
				}
				if(isValidTimeFormat(timeEnd.getText()) && validDateStart && validTimeStart) {
					if(returnStartDate().after(returnEndDate())) {
						validationLabel.setText("The Start Date must be before the End Date.");
						timeEnd.setBackground(Color.PINK);
						validTimeEnd = false;
						return false;
					}
					if(returnStartDate().equals(returnEndDate())) {
						validationLabel.setText("The Start Date cannot be equal to the End Date.");
						timeEnd.setBackground(Color.PINK);
						validTimeEnd = false;
						return false;
					}
				}
				if(isValidTimeFormat(timeEnd.getText())) {
				validTimeEnd = true;
				timeEnd.setBackground(Color.WHITE);
				}
				return true;
			} catch(NumberFormatException e) {
				validationLabel.setText("Incorrect End Date Format. Required Format(MM/DD/YYYY)");
				return false;
			}
		}
		
		/**
		 * Determines whether the given time is valid. The method
		 * checks the format of the time as well as the validity 
		 * of the date and whether the date comes after the 
		 * start date and time if the current start date and time is 
		 * valid.
		 * @return The validity of the time
		 */
		public boolean isValidEndTime() {
			try {
				if(!isValidTimeFormat(timeEnd.getText())) {
					validationLabel.setText("Incorrect End Time Format. Required Format(HH:MM(AM/PM))");
					return false;
				}
				if(validDateStart && validDateEnd && validTimeStart) {
					if(returnStartDate().after(returnEndDate())) {
						validationLabel.setText("The Start Date must be before the End Date.");
						dateEnd.setBackground(Color.PINK);
						validDateEnd = false;
						return false;
					}
					if(returnStartDate().equals(returnEndDate())) {
						validationLabel.setText("The Start Date cannot be equal to the End Date.");
						dateEnd.setBackground(Color.PINK);
						validDateEnd = false;
						return false;
					}
				}
				
				if(isValidDateFormat(dateEnd.getText())){
					validDateEnd = true;
					dateEnd.setBackground(Color.WHITE);
				}
				return true;
			} catch (NumberFormatException e) {
				validationLabel.setText("Incorrect End Time Format. Required Format(HH:MM(AM/PM))");
				return false;
			}
		}
		
		/**
		 * Returns whether the component should yield focus
		 * to other components. It will always evaluate to
		 * true to allow user to manipulate other fields
		 */
		public boolean shouldYieldFocus(JComponent comp) {
			boolean inputOK = verify(comp);
			if(inputOK) {
				validationLabel.setText("   ");
				((JTextField) comp).setBackground(Color.WHITE);
				return true;
			} else {
				((JTextField) comp).setBackground(Color.PINK);
				return true;
			}
		}
		
		/**
		 * Performs the validation operations upon detecting
		 * an ActionEvent.
		 */
		public void actionPerformed(ActionEvent e) {
			JTextField source = (JTextField) e.getSource();
			shouldYieldFocus(source);
			source.selectAll();
		}
	}
	
}
